#pragma once
#include <cstdint>
#include <functional>
#include <memory>
#include <set>
#include <thread>
#include "asio.hpp"
#include "asio/steady_timer.hpp"

namespace UTILS {
using namespace asio;
typedef std::function<void()> TimerHandler_t;

class TimerInfo {
 public:
  explicit TimerInfo(io_context &io) : timer_(io){};
  ~TimerInfo() { handler_ = nullptr; }
  std::function<void(const asio::error_code &)> handler_;
  steady_timer timer_;
};

class TimerId {
 public:
  TimerId(){};
  explicit TimerId(std::shared_ptr<TimerInfo> timer) : timer_(timer) {}
  ~TimerId() = default;

  void destory();
  [[nodiscard]] bool valid() const;
  friend class Timer;

 private:
  std::weak_ptr<TimerInfo> timer_;
};

class Timer {
 public:
  TimerId runEvery(int64_t interval, TimerHandler_t handler);

  TimerId runAfter(int64_t delay, TimerHandler_t handler);

  void cancel(TimerId &timer);

  void clear();

  bool isTimerExpired(TimerId &timer);

  static Timer &getInstance();
  static long long int get_time_ms();
  static int64_t currentTime();

 private:
  Timer();
  ~Timer();

 private:
  std::mutex mtx_;
  std::thread thd_;
  io_context io_;
  executor_work_guard<io_context::executor_type> workGuard_;
  std::set<std::shared_ptr<TimerInfo>> timers_;
};
}  // namespace UTILS